﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;

namespace NetScanService
{
    public partial class NetScanService : ServiceBase
    {
        #region DllImport

        [DllImport("Iphlpapi.dll")]
        private static extern int SendARP(Int32 dest, Int32 host, ref Int64 mac, ref Int32 length);

        [DllImport("Ws2_32.dll")]
        private static extern Int32 inet_addr(string ip);

        #endregion

        #region Atrributes

        private static string StartIP = ConfigurationManager.AppSettings["IPStart"].ToString();
        private static string EndIP = ConfigurationManager.AppSettings["IPEnd"].ToString();
        private static string StartTime = ConfigurationManager.AppSettings["TimeStart"].ToString();
        private static string EndTime = ConfigurationManager.AppSettings["TimeEnd"].ToString();
        private static string MyVal = ConfigurationManager.AppSettings["InterVal"].ToString();
        private static string LogDir = ConfigurationManager.AppSettings["LogDir"].ToString();
        private static string LogPath = LogDir + @"\ScanRecord.log";
        private static string RecordType = ConfigurationManager.AppSettings["RecordType"].ToString();
        private static string ErrorLog = ConfigurationManager.AppSettings["ErrorLog"].ToString();
        private static long StartIPLong = 0;
        private static long EndIPLong = 0;
        private static long IPCount = 0;
        private static long CurrentIPLong = 0;

        private static bool IsRunning = false;

        private static int ThreadCount = 0;
        private Thread[] PingThread;

        DateTime TimeStart, TimeEnd, Now;

        public struct ScanItem
        {
            public string ip;
            public string status;
            public string mac;
        }

        #endregion

        #region Service

        public NetScanService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            // TODO: 在此处添加代码以启动服务。
            InitService();
        }

        protected override void OnStop()
        {
            // TODO: 在此处添加代码以执行停止服务所需的关闭操作。
            this.timer1.Enabled = false;
            for (int i = 0; i < PingThread.Length; i++)
            {
                PingThread[i].Abort();
            }
        }

        private void InitService()
        {
            //通过建立目录判断用户数据的目录路径是否存在
            if (!Directory.Exists(LogDir))
            {
                OutLog(ErrorLog, "日志目录不存在");
                return;
            }

            //判断IP是否合法
            if (!(QT.Net.NetWork.CheckIP(StartIP)))
            {
                OutLog(ErrorLog, "开始IP\"" + StartIP + "\"不是合法IP地址");
                return;
            }
            if (!(QT.Net.NetWork.CheckIP(EndIP)))
            {
                OutLog(ErrorLog, "结束IP\"" + EndIP + "\"不是合法IP地址");
                return;
            }
            if (!Regex.IsMatch(MyVal, @"^\d*$"))
            {
                OutLog(ErrorLog, "重复间隔时间只能为正整数");
                return;
            }
            if (!int.TryParse(ConfigurationManager.AppSettings["ThreadCount"], out ThreadCount))
            {
                OutLog(ErrorLog, "线程数格式错误");
                return;
            }
            LogDir += "\\" + DateTime.Now.ToString("yyyy-MM-dd");
            if (!Directory.Exists(LogDir))
            {
                try
                {
                    Directory.CreateDirectory(LogDir);
                }
                catch (Exception ex)
                {
                    OutLog(ErrorLog, ex.Message);
                    return;
                }
            }
            
            if (ThreadCount > 254) ThreadCount = 254;

            StartIPLong = IPToLong(StartIP);
            EndIPLong = IPToLong(EndIP);
            IPCount = EndIPLong - StartIPLong + 1;

            if (IPCount < ThreadCount) ThreadCount = (int)IPCount;
            PingThread = new Thread[ThreadCount];

            TimeStart = Convert.ToDateTime(StartTime);
            TimeEnd = Convert.ToDateTime(EndTime);

            this.timer1.Interval = 1000;
            this.timer1.Enabled = true;
        }

        #endregion

        #region Thread

        private void Scan()
        {
            LogPath = LogDir + "\\PingRecord" + DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss") + ".log";

            CurrentIPLong = StartIPLong;
            for (int i = 0; i < PingThread.Length; i++)
            {
                PingThread[i] = new Thread(new ThreadStart(IPPingReply));
                PingThread[i].IsBackground = true;
                PingThread[i].Start();
            }
        }

        private void IPPingReply()
        {
            ScanItem Item;

            long ip = GetIP();

            while (ip != 0)
            {
                try
                {
                    Item = ApiGetMac(LongToIP(ip));
                    if ((RecordType == "1" || RecordType == "0") && Item.status == "在线")
                    {
                        OutLog(LogPath, "[" + DateTime.Now.ToLongTimeString() + "] " + Item.ip + " " + Item.status + " " + Item.mac);
                    }
                    if ((RecordType == "2" || RecordType == "0") && Item.status == "离线")
                    {
                        OutLog(LogPath, "[" + DateTime.Now.ToLongTimeString() + "] " + Item.ip + " " + Item.status + " " + Item.mac);
                    }
                }
                catch (ThreadAbortException)
                {
                }
                catch (InvalidOperationException)
                {
                }
                catch (Exception ex)
                {
                    OutLog(ErrorLog, "系统错误" + ex.Message.ToString());
                    return;
                }

                ip = GetIP();
            }

            if (GetThreadCount() == 0)
            {
                IsRunning = false;
            }
        }

        private long GetIP()
        {
            long ip = 0;
            lock (this)
            {
                if (CurrentIPLong <= EndIPLong)
                {
                    ip = CurrentIPLong;
                    CurrentIPLong++;
                }
                else
                {
                    IsRunning = false;
                }
            }
            return ip;
        }

        private int GetThreadCount()
        {
            int i = 0;
            lock (this)
            {
                ThreadCount--;
                i = ThreadCount;
            }
            return i;
        }

        #endregion

        #region IPTrans

        public static long IPToLong(string ip_Long)
        {
            char[] separator = new char[] { '.' };
            if (ip_Long.Split(separator).Length == 3)
            {
                ip_Long = ip_Long + ".0";
            }
            string[] strArray = ip_Long.Split(separator);
            long num2 = ((long.Parse(strArray[0]) * 0x100L) * 0x100L) * 0x100L;
            long num3 = (long.Parse(strArray[1]) * 0x100L) * 0x100L;
            long num4 = long.Parse(strArray[2]) * 0x100L;
            long num5 = long.Parse(strArray[3]);
            return (((num2 + num3) + num4) + num5);
        }

        public static string LongToIP(long ip_Int)
        {
            long num = (long)((ip_Int & 0xff000000L) >> 0x18);
            if (num < 0L)
            {
                num += 0x100L;
            }
            long num2 = (ip_Int & 0xff0000L) >> 0x10;
            if (num2 < 0L)
            {
                num2 += 0x100L;
            }
            long num3 = (ip_Int & 0xff00L) >> 8;
            if (num3 < 0L)
            {
                num3 += 0x100L;
            }
            long num4 = ip_Int & 0xffL;
            if (num4 < 0L)
            {
                num4 += 0x100L;
            }
            return (num.ToString() + "." + num2.ToString() + "." + num3.ToString() + "." + num4.ToString());
        }

        #endregion

        #region Timer

        /// <summary>
        /// Timer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Now = DateTime.Now;

            if (Now >= TimeStart && TimeEnd > Now)
            {
                TimeStart = Now.AddMinutes(Convert.ToDouble(MyVal));
                if (!IsRunning)
                {
                    Scan();
                }
            }
            if (TimeEnd < Now)
            {
                TimeStart = Convert.ToDateTime(StartTime);
                TimeEnd = Convert.ToDateTime(EndTime);
            }
        }

        #endregion

        #region Method

        private ScanItem ApiGetMac(string ip)
        {
            ScanItem newItem = new ScanItem();
            newItem.ip = ip;

            Int64 macinfo = new Int64();
            Int32 length = 6;
            string mac = "";
            try
            {
                SendARP(inet_addr(ip), 0, ref macinfo, ref length);
                if (macinfo == 0) newItem.mac = "";
                mac = macinfo.ToString("X12");
                if (mac != "0000" && mac != "000000000000" && mac.Length == 12)
                {
                    newItem.mac = new string(new char[] { mac[10], mac[11], '-', mac[8], mac[9], '-', mac[6], mac[7], '-', mac[4], mac[5], '-', mac[2], mac[3], '-', mac[0], mac[1] });
                    newItem.status = "在线";
                }
                else
                {
                    newItem.mac = "";
                    newItem.status = "离线";
                }
            }
            catch
            {
                newItem.mac = "";
                newItem.status = "离线";
            }

            return newItem;
        }

        private Int64 IP2Int(string ip)
        {
            Int64 newIP = 0;
            string[] IPs = ip.Split('.');
            try
            {
                newIP = Convert.ToInt64(IPs[0].PadLeft(3, '0') + IPs[1].PadLeft(3, '0') + IPs[2].PadLeft(3, '0') + IPs[3].PadLeft(3, '0'));
                return newIP;
            }
            catch
            {
                return 0;
            }
        }

        private string Int2IP(Int64 I)
        {
            string IP;
            string str = I.ToString().PadLeft(12, '0');
            IP = int.Parse(str.Substring(0, 3)).ToString() + "." + int.Parse(str.Substring(3, 3)).ToString() + "." + int.Parse(str.Substring(6, 3)).ToString() + "." + int.Parse(str.Substring(9, 3)).ToString();
            return IP;
        }

        private void OutLog(string txtName, string txt)
        {
            lock (this)
            {
                FileStream fs = new FileStream(txtName, FileMode.Append, FileAccess.Write, FileShare.Read);
                StreamWriter text = new StreamWriter(fs);
                text.WriteLine(txt);
                text.Close();
                text.Dispose();
                fs.Close();
                fs.Dispose();
            }
        }

        #endregion
    }
}
